package io.github.luons.mediator.core.validator;

import lombok.extern.slf4j.Slf4j;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * EnumMember validator
 *
 * @author : luons
 */
@Slf4j
public class EnumMemberValidator implements ConstraintValidator<EnumMember, Object> {

    /**
     * annotation
     */
    private EnumMember annotation;

    /**
     * Initializes the validator in preparation for {@link #isValid(Object, ConstraintValidatorContext)} calls. The
     * constraint annotation for a given constraint declaration is passed.
     * <p>
     * This method is guaranteed to be called before any use of this instance for validation.
     * <p>
     * The default implementation is a no-op.
     *
     * @param constraintAnnotation annotation instance for a given constraint declaration
     */
    @Override
    public void initialize(EnumMember constraintAnnotation) {
        this.annotation = constraintAnnotation;
    }

    /**
     * Implements the validation logic. The state of {@code value} must not be altered.
     * <p>
     * This method can be accessed concurrently, thread-safety must be ensured by the implementation.
     *
     * @param value   object to validate
     * @param context context in which the constraint is evaluated
     * @return {@code false} if {@code value} does not pass the constraint
     */
    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        if (value == null) {
            return true;
        }
        Class<?> clazz = this.annotation.value();
        if (!clazz.isEnum()) {
            return false;
        }

        List<Object> dataObjList = new ArrayList<>();
        if ((value instanceof List)) {
            dataObjList.addAll(((List<?>) value));
        } else {
            dataObjList.add(value);
        }
        boolean result = false;
        Object[] objects = clazz.getEnumConstants();
        try {
            Method method = clazz.getMethod(this.annotation.method());
            for (Object dataObj : dataObjList) {
                for (Object object : objects) {
                    Object invValue = method.invoke(object);
                    log.debug("{}.{} invokeValue: {} {}.{} value: {}.", clazz.getSimpleName(), object, invValue
                            , clazz.getSimpleName(), object, dataObj);
                    if ((dataObj instanceof String) && ((String) dataObj).equalsIgnoreCase(String.valueOf(invValue))) {
                        result = true;
                        break;
                    } else if (dataObj.equals(invValue)) {
                        result = true;
                        break;
                    }
                }
                if (!result) {
                    return false;
                }
            }
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }

}
